package com.ushine.web.security.utils;

import com.ushine.web.component.exception.ServiceException;
import com.ushine.web.component.exception.ServiceExceptionEnum;
import lombok.extern.slf4j.Slf4j;

import javax.crypto.Cipher;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

/**
 * @Author: yls
 * @Date: 2021/8/19 10:39
 * @Description: 【RSA】非对称加密工具类
 * @Version 1.0
 */
@Slf4j
public class RsaUtils {

    private RsaUtils() {

    }

    /**
     * 加密算法
     */
    private static final String ENCRYPT_ALGORITHM = "RSA";

    /**
     * 密钥长度
     */
    private static final int DEFAULT_KEY_SIZE = 2048;

    /**
     * 功能描述:
     * 〈从文件中读取公钥〉
     *
     * @param filename 文件名称
     * @return : java.security.PublicKey
     * @author : yls
     * @date : 2022/03/03 10:42
     */
    static PublicKey getPublicKey(String filename) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
        byte[] bytes = readFile(filename);
        return getPublicKey(bytes);
    }

    /**
     * 功能描述:
     * 〈从文件中获取密钥〉
     *
     * @param filename 文件名称
     * @return : java.security.PrivateKey
     * @author : yls
     * @date : 2022/03/03 10:43
     */
    static PrivateKey getPrivateKey(String filename) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
        byte[] bytes = readFile(filename);
        return getPrivateKey(bytes);
    }

    /**
     * 功能描述:
     * 〈获取公钥〉
     *
     * @param bytes 密钥字节形式
     * @return : java.security.PublicKey
     * @author : yls
     * @date : 2022/03/03 10:38
     */
    private static PublicKey getPublicKey(byte[] bytes) throws NoSuchAlgorithmException, InvalidKeySpecException {
        bytes = Base64.getDecoder().decode(bytes);
        X509EncodedKeySpec spec = new X509EncodedKeySpec(bytes);
        KeyFactory factory = KeyFactory.getInstance(ENCRYPT_ALGORITHM);
        return factory.generatePublic(spec);
    }

    /**
     * 功能描述:
     * 〈获取密钥〉
     *
     * @param bytes 密钥字节形式
     * @return : java.security.PrivateKey
     * @author : yls
     * @date : 2022/03/03 10:39
     */
    private static PrivateKey getPrivateKey(byte[] bytes) throws NoSuchAlgorithmException, InvalidKeySpecException {
        bytes = Base64.getDecoder().decode(bytes);
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(bytes);
        KeyFactory factory = KeyFactory.getInstance(ENCRYPT_ALGORITHM);
        return factory.generatePrivate(spec);
    }

    /**
     * 功能描述:
     * 〈根据密文，生成RSA公钥和密钥，并写入文件〉
     *
     * @param publicKeyFilename  公钥文件路径
     * @param privateKeyFilename 私钥文件路径
     * @param secret             生成密钥的密文
     * @return : void
     * @author : yls
     * @date : 2022/03/03 10:44
     */
    static void generateKey(String publicKeyFilename, String privateKeyFilename, String secret) throws NoSuchAlgorithmException, IOException, ServiceException {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ENCRYPT_ALGORITHM);
        SecureRandom secureRandom = new SecureRandom(secret.getBytes());
        keyPairGenerator.initialize(Math.max(0, DEFAULT_KEY_SIZE), secureRandom);
        KeyPair keyPair = keyPairGenerator.genKeyPair();

        // 获取公钥并写出
        byte[] publicKeyBytes = keyPair.getPublic().getEncoded();
        publicKeyBytes = Base64.getEncoder().encode(publicKeyBytes);
        writeFile(publicKeyFilename, publicKeyBytes);

        // 获取私钥并写出
        byte[] privateKeyBytes = keyPair.getPrivate().getEncoded();
        privateKeyBytes = Base64.getEncoder().encode(privateKeyBytes);
        writeFile(privateKeyFilename, privateKeyBytes);
    }


    /**
     * 功能描述:
     * 〈 解密内容 〉
     *
     * @param content     待解密内容
     * @param private_key 私钥
     * @return : java.lang.String
     * @throws Exception 异常信息
     * @author : yls
     * @date : 2022/4/20 16:42
     */
    public static String decrypt(String content, String private_key) {
        try {
            byte[] decodeContent = org.apache.commons.codec.binary.Base64.decodeBase64(content);
            //获取规范私钥
            PrivateKey privateKey = getPrivateKey(private_key);
            //指定解密算法
            Cipher cipher = Cipher.getInstance(ENCRYPT_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            byte[] result = cipher.doFinal(decodeContent);
            return new String(result);
        } catch (Exception e) {
            log.error("解密异常：", e);
            return "";
        }
    }


    private static byte[] readFile(String filename) throws IOException {
        return Files.readAllBytes(new File(filename).toPath());
    }

    private static void writeFile(String filename, byte[] bytes) throws IOException, ServiceException {
        File file = new File(filename);
        File fileParent = file.getParentFile();
        if (!file.exists()) {
            if (!fileParent.exists()) {
                boolean mkdirs = fileParent.mkdirs();
                if (Boolean.FALSE.equals(mkdirs)) {
                    throw new ServiceException("文件夹创建失败！");
                }
            }
            boolean newFile = file.createNewFile();
            if (!newFile) {
                log.error("秘钥文件创建失败：{}", filename);
                throw new ServiceException(ServiceExceptionEnum.RSA_FILE_CREATE_ERROR);
            }
        }
        Files.write(file.toPath(), bytes);
    }
}
